import { RollifyConfigure } from '../typing'
import { getLoader } from '../utils'
import { RollupAliasOptions } from '@rollup/plugin-alias'
import { normalizePath } from '@rollup/pluginutils'
import { isArray, isObject, merge } from 'node-aux'
import { isAbsolute, join, relative, resolve } from 'path'
import { InputOption, OutputOptions, RollupOptions } from 'rollup'
import { createRequire } from 'node:module'
import { RollupReplaceOptions } from '@rollup/plugin-replace'
import { transformAlias } from '../plugins/alias'
import { transformInput } from './input'
import { pathExistsSync } from 'fs-extra'
const require = createRequire(import.meta.url)
const replaceTypePath = (typePath: string, input: string) => {
    let inputPath = input
    if (isAbsolute(input)) {
        inputPath = normalizePath(relative(resolve('.'), input))
    }
    const inputFile = inputPath.replace(/\.tsx?$/, '.d.ts')
    return join(typePath, inputFile)
}

export const transformInputFile = function (input: RollupOptions['input'], typePath: string = '') {
    if (!typePath) {
        return input
    }
    if (typeof input === 'string') {
        return replaceTypePath(typePath, input)
    }
    if (isObject(input)) {
        const inputObj = {}
        Object.keys(input).forEach((key) => {
            inputObj[key] = replaceTypePath(typePath, input[key])
        })
        return inputObj
    }
    if (isArray(input)) {
        return input.map((path) => {
            return transformInputFile(path, typePath)
        })
    }
    return []
}

export const transformOutputFile = function (
    output?: OutputOptions | OutputOptions[],
    outputType?: OutputOptions
) {
    if (Array.isArray(output) && output.length) {
        const outputItem = output.find((item: OutputOptions) =>
            ['esm', 'es'].includes(String(item.format))
        )
        return merge(outputItem, outputType)
    }
    if (isObject(output)) {
        return merge({ ...output, format: 'esm' }, outputType)
    }
    return output
}

export const generateTypescript = function <T extends RollifyConfigure>(
    service: boolean,
    userConfig: T,
    input?: InputOption,
    output?: OutputOptions | OutputOptions[]
) {
    const {
        alias: aliasRecord,
        external,
        preserveEntrySignatures,
        define,
        declaration
    } = userConfig
    const { declarationDir = '', output: outputType, entry, ...options } = { ...declaration }
    const alias = getLoader<RollupAliasOptions>('@rollup/plugin-alias')
    const replace = getLoader<RollupReplaceOptions>('@rollup/plugin-replace')
    const del = getLoader('rollup-plugin-delete')
    const { default: dts } = require('rollup-plugin-dts')
    const nodeResolve = getLoader('@rollup/plugin-node-resolve')
    const commonjs = getLoader('@rollup/plugin-commonjs')
    const plugins = [
        alias({
            entries: transformAlias(aliasRecord)
        }),
        replace({
            preventAssignment: true,
            values: {
                ...define
            }
        }),
        nodeResolve({
            preferBuiltins: true
        }),
        commonjs({
            transformMixedEsModules: true,
            include: [/node_modules/]
        }),
        dts()
    ]

    if (!service && declarationDir && pathExistsSync(resolve(declarationDir))) {
        plugins.push(del({ hook: 'buildEnd', targets: declarationDir }))
    }
    const inputValue = entry
        ? transformInput('pattern', entry)
        : transformInputFile(input, declarationDir)

    return {
        input: inputValue,
        output: transformOutputFile(output, outputType),
        ...options,
        external: external || [],
        preserveEntrySignatures,
        plugins: plugins.filter(Boolean)
    }
}
